#include <bits/stdc++.h>

using namespace std;

typedef struct Node {
    int id;
    float value;
} Node ;

int compare (const void *node1, const void *node2) {
    Node *n1 = (Node *) node1;
    Node *n2 = (Node *) node2;
    // -1 代表不交换位置，1 代表交换位置
    if (n1 -> value < n2 -> value) return 1;
    else return -1;
}

float k_power (int power) {
	float result = 1.0;
	//cout << power << endl;
	if (power >= 0) {
		result = 1 << power;
	} else {
		for (int i = power; i < 0; i ++) {
			result /= 2.0;
		}
	}
	return result;
}
float float16_to_float32 (int a) {
	// a: 0x0000ffff
	float result = 0.0;
	int ratio = ((a >> 10) & 0x0000001f) - 15;

	result += 1.0 * k_power(ratio);
	for (int i = 0; i < 10; i ++) {
		result += ((a >> i) & 0x00000001) * k_power(ratio - 10 + i);
	}

	return result;
}

void check_degree (int edge_degree) {
    edge_degree &= 0xffffe000;
    float *a = (float *)(&edge_degree);
    int b = (edge_degree >> 13) & 0x00003fff;
    b |= ((edge_degree >> 16) & 0x0000c000);
    if (*a - float16_to_float32(b) > 2 || edge_degree < 1.0) {
        printf ("32_Degree = %x-%f, 16_Degree = %x-%f\n", edge_degree, *a, b, float16_to_float32(b));
        exit (-1);
    }
}

int main(int argc, char **argv) {
    FILE *p1, *p2, *p3, *p4;
    int vertex_num, edge_num;
    int updated;
    int iterator = 0;
    int *edge_offset_mem_array;
    int *edge_mem_array;
    int *edge_out_degree;
    float *vertex_p_1, *vertex_p_2;
    
    if (argc != 6) {
        cout << "Usage: Parameter (offset_file, info_file, degree_file, max_iterator_num, ori_result_file)." << endl;
        exit (-1);
    }

	if ((p1 = fopen(argv[1], "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << argv[1] << endl;
        exit(-1);
    }
    cout << "[INFO] Open file " << argv[1] << endl;

	if ((p2 = fopen(argv[2], "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << argv[2] << endl;
        exit(-1);
    }
    cout << "[INFO] Open file " << argv[2] << endl;

    if ((p3 = fopen(argv[3], "r")) == NULL) {
        cout << "[ERROR] Failed to open file " << argv[3] << endl;
        exit(-1);
    }
    cout << "[INFO] Open file " << argv[3] << endl;

    if ((p4 = fopen(argv[5], "w")) == NULL) {
        cout << "[ERROR] Failed to open file " << argv[5] << endl;
        exit(-1);
    }
    cout << "[INFO] Open file " << argv[5] << endl;

    iterator = atoi (argv[4]);
	fscanf(p1, "%d", &vertex_num);
    fscanf(p2, "%d", &edge_num);

    cout << "[INFO] Vertex_num = " << vertex_num << ", Edge_num = " << edge_num << ", Max_Iterator_num = " << iterator << endl;

    edge_offset_mem_array = (int *) calloc (vertex_num + 1, sizeof(int));
    edge_mem_array = (int *) calloc (edge_num, sizeof(int));
    edge_out_degree = (int *) calloc (edge_num, sizeof(int));
    vertex_p_1 = (float *) calloc (vertex_num, sizeof(float));
    vertex_p_2 = (float *) calloc (vertex_num, sizeof(float));

	for (int i = 0; i < (vertex_num + 1); ++ i) {
	    fscanf(p1, "%d", &edge_offset_mem_array[i]);
	    if (i && (edge_offset_mem_array[i] == edge_offset_mem_array[i - 1])) {
            cout << "[ERROR] Offset init failed: Vertex " << i << ", Off_l = " << edge_offset_mem_array[i - 1] << ", Off_r = " << edge_offset_mem_array[i] << endl;
            exit (-1);
        }
	}

	for (int i = 0; i < edge_num; ++ i) fscanf(p2, "%d", &edge_mem_array[i]);
    for (int i = 0; i < edge_num; ++ i) fscanf(p3, "%d", &edge_out_degree[i]);

	fclose(p1);
	fclose(p2);
	fclose(p3);

    for (int i = 0; i < vertex_num; ++ i) {
        vertex_p_1[i] = 1.0;
        vertex_p_2[i] = 1.0;
    }

    cout << "0x3acc = " << float16_to_float32(0x00003acc) << ", 0x30cc = " << float16_to_float32(0x000030cc) << endl;

    assert (iterator > 0 && iterator < 10000);
    int change = 0; // 0: Use p1, 1 : Use p2
    for (int iter = 0; iter < iterator; ++ iter) {

        int cn = 0;
        for (int i = 0; i < vertex_num; ++ i) {
            int left = edge_offset_mem_array[i];
            int right = edge_offset_mem_array[i + 1];
            float tmp = 0;
            float *vertex_p_i, *vertex_p_o;
            vertex_p_i = change ? vertex_p_2 : vertex_p_1;
            vertex_p_o = change ? vertex_p_1 : vertex_p_2;

            for (int j = left; j < right; ++ j) {
                float *d = (float *)(&edge_out_degree[j]);
                float re = vertex_p_i[edge_mem_array[j]] / (*d);
                tmp += re;
            }
            float k = tmp * 0.85 + 0.15;
            float t;
            if (vertex_p_i[i] > k) t = vertex_p_i[i] - k;
            else t = k - vertex_p_i[i];
            if (t > 0.00000001) cn = 1;
            vertex_p_o[i] = k;
        }
        if (!cn) {
            cout << "Convergence Iterator Number = " << iter << endl;
            break;
        } else if (iter == iterator - 1) {
            cout << "PR not convergence." << endl;
        }
        change = !change;
    }

/*
	for (int iter = 0; iter < iterator; ++ iter) {

        int cn = 0;
        for (int i = 0; i < vertex_num; ++ i) {
            int left = edge_offset_mem_array[i];
            int right = edge_offset_mem_array[i + 1];
            float tmp = 0;
            int *a = (int *)(&tmp);

            for (int j = left; j < right; ++ j) {
                float *d = (float *)(&edge_out_degree[j]);
                float re = vertex_p[edge_mem_array[j]] / (*d);
                //int *b = (int *)(&re);
                //(*b) &= 0xffffe000;
                tmp += re;
            }
            // 转换为 16 精度
            //(*a) &= 0xffffe000;

            //float k = tmp * float16_to_float32(0x00003acc) + float16_to_float32(0x000030cc);
            float k = tmp * 0.85 + 0.15;
            // int *k1 = (int *)(&k);
            // *(k1) &= 0xffff8000;
            float t;
            if (vertex_p[i] > k) t = vertex_p[i] - k;
            else t = k - vertex_p[i];
            if (t > 0.00000001) cn = 1;
            vertex_p[i] = k;
        }
        if (!cn) {
            cout << "Convergence Iterator Number = " << iter << endl;
            break;
        } else if (iter == iterator - 1) {
            cout << "PR not convergence." << endl;
        }
	}
*/
    float *vertex_p = change ? vertex_p_2 : vertex_p_1;
	for (int i = 0; i < vertex_num; ++ i) fprintf(p4, "%d %f\n", i, vertex_p[i]);
	fclose(p4);
}
